package com.hucc.redislock;

import com.hucc.redislock.command.AddLockRedisCommand;
import com.hucc.redislock.command.DelLockRedisCommand;
import com.hucc.redislock.command.SubscribeLockReleaseRedisCommand;
import com.hucc.redislock.result.CommandResult;
import redis.clients.jedis.JedisPubSub;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * redis 可重入锁
 */
public class RedisReentrantLock implements RedisLock{

    private LockExpireManager lockExpireManager = new LockExpireManager();

    private Yaohao yaohao = new Yaohao();

    private String id = UUID.randomUUID().toString();

    private String name;

    public RedisReentrantLock(String name) {
        this.name = name;
        lockExpireManager.register(name);
        subscriberLockRelease();
    }

        @Override
    public void lock() {
        CommandResult<Long> commandResult = tryAcquire();

        while (!commandResult.isSuccess()) {

            Long lockTtl = commandResult.getData();
            yaohao.turn(lockTtl);

            commandResult = tryAcquire();
        }
    }

    private CommandResult<Long> tryAcquire() {
        AddLockRedisCommand addLockRedisCommand = new AddLockRedisCommand(name, getThreadName());
        CommandResult result = RedisLockContext.getRedisCommandExecutor().executeCommand(addLockRedisCommand);
        if (result.isSuccess()) {
            yaohao.clear();
            lockExpireManager.automaticExtension(getThreadName());
        }

        return result;
    }

    private String getThreadName() {
        return id + ":" + Thread.currentThread().getId();
    }

    @Override
    public void unlock() {
        DelLockRedisCommand delLockRedisCommand = new DelLockRedisCommand(name, getThreadName());
        CommandResult commandResult = RedisLockContext.getRedisCommandExecutor().executeCommand(delLockRedisCommand);
        if (commandResult.isSuccess()) {
            //锁被删除成功，停止锁过期时间自动重置
            if (commandResult.getCode() == 1) {
                lockExpireManager.stopExtension();
            }
        }
    }

    @Override
    public boolean tryLock() {
        CommandResult<Long> commandResult = tryAcquire();
        return commandResult.isSuccess();
    }

    @Override
    public boolean tryLock(long millisecond) {
        if (millisecond <= 0) {
            return false;
        }

        long endTime = System.currentTimeMillis() + millisecond;

        CommandResult<Long> commandResult = tryAcquire();

        while (!commandResult.isSuccess() && endTime > System.currentTimeMillis()) {

            Long lockTtl = commandResult.getData();
            long surplusTime = endTime - System.currentTimeMillis();
            yaohao.turn(lockTtl <= surplusTime? lockTtl: surplusTime);

            commandResult = tryAcquire();
        }

        return commandResult.isSuccess();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        CommandResult<Long> commandResult = tryAcquire();

        while (!commandResult.isSuccess()) {

            Long lockTtl = commandResult.getData();
            yaohao.turnInterruptibly(lockTtl);

            commandResult = tryAcquire();
        }
    }

    private void subscriberLockRelease() {
        SubscribeLockReleaseRedisCommand redisCommand = new SubscribeLockReleaseRedisCommand(new LockReleaseMessageSubscriber(), name);
        RedisLockContext.getRedisCommandExecutor().executeCommand(redisCommand);
    }
    /**
     * redis 锁释放消息订阅者
     */
    private class LockReleaseMessageSubscriber extends JedisPubSub {
        @Override
        public void onMessage(String channel, String message) {
            yaohao.putIn();
        }
    }

}
